package com.jxjk.reportwork.biz;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.jxjk.reportwork.exceldo.AttendanceAdjustOutExcelDO;
import com.jxjk.reportwork.exceldo.BusinessTripInExcelDO;
import com.jxjk.reportwork.exceldo.ReportPlanInExcelDO;
import com.jxjk.reportwork.listener.BusinessTripListener;
import com.jxjk.reportwork.model.*;
import com.jxjk.reportwork.util.DateUtils;
import com.jxjk.reportwork.util.ReportWorkConstant;
import com.jxjk.reportwork.util.StringFormatUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.Session;

import java.io.File;
import java.util.Date;
import java.util.List;

@Data
@Slf4j
public class BusinessTripBiz {
    private List<BusinessTripInExcelDO> businessTripInExcelDOList = Lists.newArrayList();
    private List<BusinessTrip> businessTripList = Lists.newArrayList();
    private Session session;
    private ReportConfig reportConfig;


    /**
     * 读取差旅明细excel记录
     */
    public void loadExcel() {
        BusinessTripListener businessTripListener = new BusinessTripListener();
        String businessTripFileName = ReportWorkConstant.WORK_DIR + reportConfig.getBusinessTripFile();
        log.info("替换后的文件名称, businessTripFileName: {}", businessTripFileName);

        File businessTripFile = new File(businessTripFileName);
        EasyExcel.read(businessTripFile, BusinessTripInExcelDO.class, businessTripListener).sheet("差旅明细").doRead();
        this.businessTripInExcelDOList.addAll(businessTripListener.getBusinessTripList());
        for (BusinessTripInExcelDO businessTripInExcelDO : businessTripInExcelDOList) {
//            if (StringUtils.isEmpty(businessTripExcelDO.getRealStartDate()) == false) {
            BusinessTrip businessTrip = new BusinessTrip();
            businessTrip.setStaffNo(businessTripInExcelDO.getStaffNo());
            businessTrip.setTripPerson(businessTripInExcelDO.getTripPerson());
            businessTrip.setTripStatus(businessTripInExcelDO.getTripStatus());
            businessTrip.setDepartment(businessTripInExcelDO.getDepartment());
            businessTrip.setTripNo(businessTripInExcelDO.getTripNo());
            businessTrip.setProjectName(businessTripInExcelDO.getProjectName());
            businessTrip.setFinanceNo(businessTripInExcelDO.getFinanceNo());
            businessTrip.setApplyDate(DateUtils.dateTime("yyyy-MM-dd", businessTripInExcelDO.getApplyDate()));
            businessTrip.setApplyStartDate(DateUtils.dateTime("yyyy-MM-dd", businessTripInExcelDO.getApplyStartDate()));
            businessTrip.setApplyEndDate(DateUtils.dateTime("yyyy-MM-dd", businessTripInExcelDO.getApplyEndDate()));
            if (StringUtils.isNotEmpty(businessTripInExcelDO.getRealStartDate())) {
                businessTrip.setRealStartDate(DateUtils.dateTime("yyyy-MM-dd", businessTripInExcelDO.getRealStartDate()));
            }
            if (StringUtils.isNotEmpty(businessTripInExcelDO.getRealEndDate())) {
                businessTrip.setRealEndDate(DateUtils.dateTime("yyyy-MM-dd", businessTripInExcelDO.getRealEndDate()));
            }
            session.save(businessTrip);
//            }
        }
    }

    /**
     * 分割差旅明细单；如果一个差旅单跨越1个月，则需要分割
     */
    public void splitBusinessTrip() {
        log.info("===分割出差明细开始===");
        int count = 0;
        List<BusinessTrip> businessTripList = session.createQuery(" from BusinessTrip ").list();
        for (BusinessTrip businessTrip : businessTripList) {
            if (StringUtils.equalsIgnoreCase(businessTrip.getTripStatus(), "待提交")
                    || StringUtils.equalsIgnoreCase(businessTrip.getTripStatus(), "已作废")) {
                log.info("出差单的状态不对, businessTrip:{}", businessTrip);
                continue;
            }
            Date startDate = businessTrip.getApplyStartDate();
            Date endDate = businessTrip.getApplyEndDate();
            //如果实际出差日期不为空
            if (businessTrip.getRealStartDate() != null) {
                startDate = businessTrip.getRealStartDate();
            }

            if (businessTrip.getRealEndDate() != null) {
                endDate = businessTrip.getRealEndDate();
            }

            String startMonth = DateUtils.getDateString(startDate, DateUtils.YYYY_MM);
            String endMonth = DateUtils.getDateString(endDate, DateUtils.YYYY_MM);
            //出差的起始日期和截止日期是同一个月
            if (StringUtils.equalsIgnoreCase(startMonth, endMonth)) {
                addTripDetail(businessTrip, startDate, endDate);
                count++;
            } else {
                //出差的起始日期和截止日期不是同一个月，也就是出差跨月了
                int startMonthInt = DateUtils.getMonthInt(startMonth);
                int endMonthInt = DateUtils.getMonthInt(endMonth);
                int periodMonth = endMonthInt - startMonthInt + 1;
                Date startMonthDate = DateUtils.dateTime(DateUtils.YYYY_MM, startMonth);
                log.info("【添加出差明细】startMonthInt:{}, endMonthInt:{}, periodMonth:{}", startMonthInt, endMonthInt, periodMonth);
                for (int i = 0; i < periodMonth; i++) {
                    //月初
                    Date tmpStartDate = DateUtils.addMonths(startMonthDate, i);
                    //月末
                    Date tmpEndDate = DateUtils.addDays(DateUtils.addMonths(startMonthDate, i + 1), -1);
                    if (tmpStartDate.before(startDate)) {
                        tmpStartDate = startDate;
                    }
                    if (tmpEndDate.after(endDate)) {
                        tmpEndDate = endDate;
                    }
                    log.info("【添加出差明细】tmpStartDate:{}, tmpEndDate:{}, startDate:{}, endDate:{}", tmpStartDate, tmpEndDate, startDate, endDate);
                    addTripDetail(businessTrip, tmpStartDate, tmpEndDate);
                    count++;
                }

            }
        }
        log.info("===分割出差明细结束，出差记录数：{}，出差明细总条数:{}===", businessTripList.size(), count);
    }

    private void addTripDetail(BusinessTrip businessTrip, Date startDate, Date endDate) {
        String[] tmpStaffNoArr = StringUtils.split(businessTrip.getStaffNo(), ",");
        String[] tmpStaffNameArr = StringUtils.split(businessTrip.getTripPerson(), ",");
        for (int i = 0; i < tmpStaffNoArr.length; i++) {
            String tmpStaffNo = tmpStaffNoArr[i];
            String tmpStaffName = tmpStaffNameArr[i];
            TripDetail tripDetail = new TripDetail();

            tripDetail.setStaffNo(tmpStaffNo);
            tripDetail.setStaffName(tmpStaffName);
            tripDetail.setStartDate(startDate);
            tripDetail.setEndDate(endDate);
            tripDetail.setProjectName(businessTrip.getProjectName());
            tripDetail.setProjectNo(businessTrip.getFinanceNo());
            tripDetail.setTripNo(businessTrip.getTripNo());
            tripDetail.setTripId(businessTrip.getId());
            tripDetail.setDepartment(businessTrip.getDepartment());
            List<TripDetail> tripDetailList = session.createQuery(
                    " from TripDetail where staffNo = :staffNo and startDate = :startDate")
                    .setString("staffNo", tripDetail.getStaffNo())
                    .setDate("startDate", tripDetail.getStartDate())
                    .list();
            if (CollectionUtils.isEmpty(tripDetailList)) {
                session.save(tripDetail);
                log.info("添加出差明细:{}", tripDetail);
            }
        }
    }

    /**
     * 生成考勤调整单
     *
     * @param reportPlanInExcelDOList
     * @param workMonth
     */
    public void genAttendanceAdjust(List<ReportPlanInExcelDO> reportPlanInExcelDOList, WorkMonth workMonth) {
        List<AttendanceAdjust> totalList = Lists.newArrayList();
        log.info("【导出考勤批量调整】工作月份信息, workMonth:{}", workMonth);
        //清除掉所有记录
        log.info("【导出考勤批量调整】清除掉所有考勤调整记录, workMonth:{}", workMonth);

        //根据当月的报工计划，遍历生成每个人的考勤调整记录
        for (ReportPlanInExcelDO reportPlanInExcelDO : reportPlanInExcelDOList) {
            //生成考勤调整记录
            List<AttendanceAdjust> tmpList = genAttendanceAdjustList(reportPlanInExcelDO, workMonth);
            //合并考勤调整记录
            List<AttendanceAdjust> mergedList = mergeRepeatedProject(tmpList);
            totalList.addAll(mergedList);
            log.info("【导出考勤批量调整】添加{}的考勤调整记录{}条，调整后一共：{}条",
                    reportPlanInExcelDO.getStaffName(), mergedList.size(), totalList.size());
        }

        //转化一下
        List<AttendanceAdjustOutExcelDO> totalAttendanceAdjustOutExcelDOList = Lists.newArrayList();
        for (AttendanceAdjust attendanceAdjust : totalList) {
            log.info("attendanceAdjust:{}", attendanceAdjust);
            AttendanceAdjustOutExcelDO attendanceAdjustOutExcelDO = new AttendanceAdjustOutExcelDO();
            attendanceAdjustOutExcelDO.setProjectName(attendanceAdjust.getProjectName());
            attendanceAdjustOutExcelDO.setProjectNo(attendanceAdjust.getProjectNo());
            attendanceAdjustOutExcelDO.setStartDate(attendanceAdjust.getStartDate());
            attendanceAdjustOutExcelDO.setEndDate(attendanceAdjust.getEndDate());
            attendanceAdjustOutExcelDO.setStaffName(attendanceAdjust.getStaffName());


            attendanceAdjustOutExcelDO.setStaffNo(attendanceAdjust.getStaffNo());
            attendanceAdjustOutExcelDO.setStaffType(attendanceAdjust.getStaffType());
            attendanceAdjustOutExcelDO.setPercent(attendanceAdjust.getPercent());
            attendanceAdjustOutExcelDO.setCompany(attendanceAdjust.getCompany());
            attendanceAdjustOutExcelDO.setExecuteCompany(attendanceAdjust.getExecuteCompany());

            totalAttendanceAdjustOutExcelDOList.add(attendanceAdjustOutExcelDO);
        }
        String today = DateUtils.getDateString(new Date(), "yyyy-MM-dd");
        String resultFileName = StringFormatUtil.format(ReportWorkConstant.ADJUST_RESULT_FILE, today);
        String fullResultFileName = ReportWorkConstant.WORK_DIR + resultFileName;
        File resultFile = new File(fullResultFileName);
        if (resultFile.exists()) {
            resultFile.delete();
        }
        LongestMatchColumnWidthStyleStrategy styleStrategy = new LongestMatchColumnWidthStyleStrategy();
        EasyExcel.write(resultFile, AttendanceAdjustOutExcelDO.class).registerWriteHandler(styleStrategy)
                .sheet("结果").doWrite(totalAttendanceAdjustOutExcelDOList);
    }

    /**
     * 当相邻的两条记录，项目名称相同时，则需要合并。
     * 1. 两条的项目名称相同
     * 2. 两条的项目名称与报工计划项目名称相同
     *
     * @param attendanceAdjustList
     * @return
     */
    private List<AttendanceAdjust> mergeRepeatedProject(List<AttendanceAdjust> attendanceAdjustList) {
        if (attendanceAdjustList != null && attendanceAdjustList.size() <= 1) {
            return attendanceAdjustList;
        }
        List<AttendanceAdjust> newList = Lists.newArrayList();
        AttendanceAdjust first = null;
        for (int i = 0; i < attendanceAdjustList.size(); i++) {
            if (i == 0) {
                first = attendanceAdjustList.get(i);
                newList.add(first);
            } else {
                AttendanceAdjust current = attendanceAdjustList.get(i);
                //如果项目名称相同，则需要合并，因此需要移除掉最后一个元素
                if (StringUtils.equalsIgnoreCase(first.getProjectName(), current.getProjectName())) {
                    log.info("项目名称相同，合并考勤调整记录, first:{}, current:{}", first, current);
                    AttendanceAdjust old = newList.remove(newList.size() - 1);
                    current.setStartDate(old.getStartDate());
                } else if (StringUtils.equalsIgnoreCase(first.getProjectName(), current.getOriProjectName())) {
                    log.info("项目名称与报工计划的名称有映射关系1，合并考勤调整记录 first:{}, current:{}", first, current);
                    AttendanceAdjust old = newList.remove(newList.size() - 1);
                    current.setStartDate(old.getStartDate());
                    current.setProjectName(current.getOriProjectName());
                } else if (StringUtils.equalsIgnoreCase(first.getOriProjectName(), current.getProjectName())) {
                    log.info("项目名称与报工计划的名称有映射关系2，合并考勤调整记录 first:{}, current:{}", first, current);
                    AttendanceAdjust old = newList.remove(newList.size() - 1);
                    current.setStartDate(old.getStartDate());
                    current.setProjectName(first.getOriProjectName());
                }
                newList.add(current);
                first = current;
            }
        }
        return newList;


    }

    /**
     * 针对每一条报工计划， 去差旅表里看看是否有记录；
     * 如果没有，则不用考勤调整
     * 如果有，则根据出差记录分割当月的考勤调整记录，在这个函数getDatePeriodList完成
     *
     * @param reportPlanInExcelDO
     * @param workMonth
     * @return
     */
    public List<AttendanceAdjust> genAttendanceAdjustList(ReportPlanInExcelDO reportPlanInExcelDO, WorkMonth workMonth) {
        log.info("查询出差考勤, reportPlan:{}, workMonth:{}", reportPlanInExcelDO, workMonth);
        List<AttendanceAdjust> list = Lists.newArrayList();

        //第1步，先获取某人的出差明细记录
        TripDetail queryTrip = new TripDetail();
        queryTrip.setStaffNo(reportPlanInExcelDO.getStaffNo());
        queryTrip.setStaffName(reportPlanInExcelDO.getStaffName());
        queryTrip.setStartDate(workMonth.getMonthStartDate());
        queryTrip.setEndDate(workMonth.getMonthEndDate());
        //这里都是切割好的出差明细，报工明细的时间跨度不可能超过1个月
        List<TripDetail> tripDetailList = session.createQuery("from TripDetail where staffNo = :staffNo and startDate >= :startDate and endDate <= :endDate")
                .setString("staffNo", queryTrip.getStaffNo())
                .setDate("startDate", queryTrip.getStartDate())
                .setDate("endDate", queryTrip.getEndDate())
                .list();
        if (CollectionUtils.isEmpty(tripDetailList)) {
            log.info("没有出差明细，忽略, queryTrip:{}", queryTrip);
            return Lists.newArrayList();
        }


        //第2步：根据出差时间段，计算出剩余的考勤时间段
        List<StaffDatePeriod> staffDatePeriodList = getDatePeriodList(tripDetailList, workMonth);


        //第3步，根据考勤时间段，得到他的考勤调整表格
        for (StaffDatePeriod staffDatePeriod : staffDatePeriodList) {
            AttendanceAdjust attendanceAdjust = new AttendanceAdjust();
            attendanceAdjust.setCompany("智能云事业部");
            attendanceAdjust.setExecuteCompany("智能云事业部");
            attendanceAdjust.setPercent("100%");
            attendanceAdjust.setStaffType("中心");
            attendanceAdjust.setTripNo(staffDatePeriod.getTripNo());
            attendanceAdjust.setTripDetailId(staffDatePeriod.getTripDetailId());
            attendanceAdjust.setStartDate(staffDatePeriod.getStartDate());
            attendanceAdjust.setEndDate(staffDatePeriod.getEndDate());
            attendanceAdjust.setWorkMonth(workMonth.getMonth());
            attendanceAdjust.setStaffNo(reportPlanInExcelDO.getStaffNo());
            attendanceAdjust.setStaffName(reportPlanInExcelDO.getStaffName());
            attendanceAdjust.setProjectName(reportPlanInExcelDO.getProjectName());


            //如果是出差调整记录，则用出差项目信息覆盖项目信息
            if (StringUtils.isNotEmpty(staffDatePeriod.getTripNo())) {
                BusinessTrip businessTrip = (BusinessTrip) session.createQuery("from BusinessTrip where tripNo = :tripNo")
                        .setString("tripNo", staffDatePeriod.getTripNo())
                        .uniqueResult();
                attendanceAdjust.setProjectNo(businessTrip.getFinanceNo());
                attendanceAdjust.setProjectName(businessTrip.getProjectName());
            }


            AttendanceAdjust adjustQuery = new AttendanceAdjust();
            adjustQuery.setStaffNo(attendanceAdjust.getStaffNo());
            adjustQuery.setStartDate(attendanceAdjust.getStartDate());
            //判断考勤调整记录是否存在
            List<AttendanceAdjust> attendanceAdjustList = session.createQuery("from AttendanceAdjust where staffNo = :staffNo and startDate = :startDate")
                    .setString("staffNo", adjustQuery.getStaffNo())
                    .setDate("startDate", adjustQuery.getStartDate())
                    .list();
            if (CollectionUtils.isEmpty(attendanceAdjustList)) {
                session.save(attendanceAdjust);
            }

            list.add(attendanceAdjust);
        }

        return list;
    }

    /**
     * 按月来分割
     * 一条差旅记录，最多得到3条考勤调整明细
     *
     * @param tripDetailList
     * @param workMonth
     * @return
     */
    private List<StaffDatePeriod> getDatePeriodList(List<TripDetail> tripDetailList, WorkMonth workMonth) {
        List<StaffDatePeriod> staffDatePeriodList = Lists.newArrayList();
        Date nextStartDate = null;
        //每条出差考勤记录，最多产生2条考勤调整记录：
        //1. 出差记录本身的； 2.出差记录之前的；
        //出差记录（出差日期之后的），最后补充
        for (int i = 0; i < tripDetailList.size(); i++) {
            TripDetail tripDetail = tripDetailList.get(i);
            if (i == 0) { //第一个出差明细记录
                //出差记录的起始时间，大于1号
                if (tripDetail.getStartDate().after(workMonth.getMonthStartDate()) == true) {
                    //出差记录之前的；
                    Date tmpStartDate = workMonth.getMonthStartDate();
                    Date tmpEndDate = DateUtils.addDays(tripDetail.getStartDate(), -1);
                    StaffDatePeriod staffDatePeriod = new StaffDatePeriod();
                    staffDatePeriod.setStartDate(tmpStartDate);
                    staffDatePeriod.setEndDate(tmpEndDate);
                    staffDatePeriodList.add(staffDatePeriod);
                    log.info("【第一条出差记录】11.staffDatePeriod:{}, tripDetail:{}", staffDatePeriod, tripDetail);

                    //出差记录本身的；
                    staffDatePeriod = new StaffDatePeriod();
                    staffDatePeriod.setStartDate(tripDetail.getStartDate());
                    staffDatePeriod.setEndDate(tripDetail.getEndDate());
                    staffDatePeriod.setTripDetailId(tripDetail.getId());
                    staffDatePeriod.setTripNo(tripDetail.getTripNo());
                    log.info("【第一条出差记录】22.staffDatePeriod:{}, tripDetail:{}", staffDatePeriod, tripDetail);

                    staffDatePeriodList.add(staffDatePeriod);
                    nextStartDate = DateUtils.addDays(tripDetail.getEndDate(), 1);
                } else {
                    //出差记录从1号开始
                    StaffDatePeriod staffDatePeriod = new StaffDatePeriod();
                    staffDatePeriod.setStartDate(tripDetail.getStartDate());
                    staffDatePeriod.setEndDate(tripDetail.getEndDate());
                    staffDatePeriod.setTripDetailId(tripDetail.getId());
                    staffDatePeriod.setTripNo(tripDetail.getTripNo());
                    log.info("【第一条出差记录】33.staffDatePeriod:{}, tripDetail:{}", staffDatePeriod, tripDetail);

                    staffDatePeriodList.add(staffDatePeriod);
                    //得到下一个时间段的起始时间
                    nextStartDate = DateUtils.addDays(tripDetail.getEndDate(), 1);
                    //如果出差明细的结束时间, 不早于月的最后一天，则直接退出
                    if (tripDetail.getEndDate().before(workMonth.getMonthEndDate()) == false) {
                        continue;
                    }
                }

            } else {
                //出差记录的起始时间，大于开始时间
                if (tripDetail.getStartDate().after(nextStartDate) == true) {
                    //出差记录之前的；
                    Date tmpStartDate = nextStartDate;
                    Date tmpEndDate = DateUtils.addDays(tripDetail.getStartDate(), -1);
                    StaffDatePeriod staffDatePeriod = new StaffDatePeriod();
                    staffDatePeriod.setStartDate(tmpStartDate);
                    staffDatePeriod.setEndDate(tmpEndDate);
                    staffDatePeriodList.add(staffDatePeriod);
                    log.info("【第N条出差记录】44.staffDatePeriod:{}, tripDetail:{}", staffDatePeriod, tripDetail);

                    //出差记录本身的；
                    staffDatePeriod = new StaffDatePeriod();
                    staffDatePeriod.setStartDate(tripDetail.getStartDate());
                    staffDatePeriod.setEndDate(tripDetail.getEndDate());
                    staffDatePeriod.setTripDetailId(tripDetail.getId());
                    staffDatePeriod.setTripNo(tripDetail.getTripNo());
                    log.info("【第N条出差记录】55.staffDatePeriod:{}, tripDetail:{}", staffDatePeriod, tripDetail);

                    staffDatePeriodList.add(staffDatePeriod);
                    nextStartDate = DateUtils.addDays(tripDetail.getEndDate(), 1);
                } else {
                    //出差记录本身的；
                    StaffDatePeriod staffDatePeriod = new StaffDatePeriod();
                    staffDatePeriod.setStartDate(tripDetail.getStartDate());
                    staffDatePeriod.setEndDate(tripDetail.getEndDate());
                    staffDatePeriod.setTripDetailId(tripDetail.getId());
                    staffDatePeriod.setTripNo(tripDetail.getTripNo());
                    log.info("【第N条出差记录】66.staffDatePeriod:{}, tripDetail:{}", staffDatePeriod, tripDetail);

                    staffDatePeriodList.add(staffDatePeriod);
                    nextStartDate = DateUtils.addDays(tripDetail.getEndDate(), 1);
                    //如果出差明细的结束时间, 不早于月的最后一天，则直接退出
                    if (tripDetail.getEndDate().before(workMonth.getMonthEndDate()) == false) {
                        break;
                    }
                }

            }
        }

        //出差记录（出差日期之后的），最后补充
        if (nextStartDate != null && nextStartDate.before(workMonth.getMonthEndDate())) {
            StaffDatePeriod staffDatePeriod = new StaffDatePeriod();
            Date tmpEndDate = workMonth.getMonthEndDate();
            Date tmpStartDate = nextStartDate;
            staffDatePeriod.setStartDate(tmpStartDate);
            staffDatePeriod.setEndDate(tmpEndDate);
            staffDatePeriodList.add(staffDatePeriod);
            log.info("【最后一条出差记录】77.staffDatePeriod:{}", staffDatePeriod);
        }
        return staffDatePeriodList;
    }
}
